Add dedicated metadata for adding -l/-L flags
authorCorey Richardson <corey@octayn.net>
Tue, 10 Mar 2015 17:12:47 +0000 (13:12 -0400)
committerCorey Richardson <corey@octayn.net>
Sat, 14 Mar 2015 11:23:13 +0000 (07:23 -0400)
Deprecates the rustc-args metadata keyword in favor of rustc-link-lib and
rustc-link-search, which are more precise and allows for easy, correct
handling of spaces in pathnames.

Closes #1015

src/cargo/ops/cargo_compile.rs
src/cargo/ops/cargo_rustc/custom_build.rs
src/cargo/util/config.rs
src/doc/build-script.md
tests/test_cargo_compile_custom_build.rs

index e848ce9f1305ed305277458da314146b6512571b..f9fd404a09e591608bd0873c593eb5a689f8f20c 100644 (file)
@@ -26,7 +26,7 @@ use std::collections::HashMap;
 use std::default::Default;
 use std::num::ToPrimitive;
 use std::os;
-use std::path::Path;
+use std::path::{Path, PathBuf};
 use std::sync::Arc;
 
 use core::registry::PackageRegistry;
@@ -34,7 +34,7 @@ use core::{Source, SourceId, PackageSet, Package, Target, PackageId};
 use core::resolver::Method;
 use ops::{self, BuildOutput, ExecEngine};
 use sources::{PathSource};
-use util::config::Config;
+use util::config::{ConfigValue, Config};
 use util::{CargoResult, internal, human, ChainError, profile};
 
 /// Contains informations about how a package should be compiled.
@@ -221,6 +221,7 @@ fn scrape_build_config(config: &Config,
 
 fn scrape_target_config(config: &Config, triple: &str)
                         -> CargoResult<ops::TargetConfig> {
+
     let key = format!("target.{}", triple);
     let ar = try!(config.get_string(&format!("{}.ar", key)));
     let linker = try!(config.get_string(&format!("{}.linker", key)));
@@ -246,16 +247,30 @@ fn scrape_target_config(config: &Config, triple: &str)
         let table = try!(config.get_table(&key)).unwrap().0;
         for (k, _) in table.into_iter() {
             let key = format!("{}.{}", key, k);
-            let (v, path) = try!(config.get_string(&key)).unwrap();
-            if k == "rustc-flags" {
-                let whence = format!("in `{}` (in {:?})", key, path);
-                let (paths, links) = try!(
-                    BuildOutput::parse_rustc_flags(&v, &whence)
-                );
-                output.library_paths.extend(paths.into_iter());
-                output.library_links.extend(links.into_iter());
-            } else {
-                output.metadata.push((k, v));
+            match try!(config.get(&key)).unwrap() {
+                ConfigValue::String(v, path) => {
+                    if k == "rustc-flags" {
+                        let whence = format!("in `{}` (in {:?})", key, path);
+                        let (paths, links) = try!(
+                            BuildOutput::parse_rustc_flags(&v, &whence)
+                        );
+                        output.library_paths.extend(paths.into_iter());
+                        output.library_links.extend(links.into_iter());
+                    } else {
+                        output.metadata.push((k, v));
+                    }
+                },
+                ConfigValue::List(a, p) => {
+                    if k == "rustc-link-lib" {
+                        output.library_links.extend(a.into_iter().map(|(v, _)| v));
+                    } else if k == "rustc-link-search" {
+                        output.library_paths.extend(a.into_iter().map(|(v, _)| PathBuf::new(&v)));
+                    } else {
+                        try!(config.expected("string", &k, ConfigValue::List(a, p)));
+                    }
+                },
+                // technically could be a list too, but that's the exception to the rule...
+                cv => { try!(config.expected("string", &k, cv)); }
             }
         }
         ret.overrides.insert(lib_name, output);
index 76ee30fb881fbee5dbd463312a41466ad57f9080..48bb6a2856c11b4257c0bf6281c900d6d7270db9 100644 (file)
@@ -287,6 +287,10 @@ impl BuildOutput {
                 );
                 library_links.extend(links.into_iter());
                 library_paths.extend(libs.into_iter());
+            } else if key == "rustc-link-lib" {
+                library_links.push(value.to_string());
+            } else if key == "rustc-link-search" {
+                library_paths.push(PathBuf::new(&value));
             } else {
                 metadata.push((key.to_string(), value.to_string()))
             }
index 7677da2dad4477a737fec15f73d78a2936e82326..b633e93660ac38573440f02c6742522096a192e2 100644 (file)
@@ -132,6 +132,14 @@ impl<'a> Config<'a> {
         }
     }
 
+    pub fn get_list(&self, key: &str) -> CargoResult<Option<(Vec<(String, PathBuf)>, PathBuf)>> {
+        match try!(self.get(key)) {
+            Some(CV::List(i, path)) => Ok(Some((i, path))),
+            Some(val) => self.expected("list", key, val),
+            None => Ok(None),
+        }
+    }
+
     pub fn get_table(&self, key: &str)
                     -> CargoResult<Option<(HashMap<String, CV>, PathBuf)>> {
         match try!(self.get(key)) {
index 6d97f578c23633a0059b04a3d6f4b752fba28b08..0ac897e970ec602d5b67a51be476801591735937 100644 (file)
@@ -77,14 +77,20 @@ are interpreted by Cargo and must be of the form `key=value`.
 Example output:
 
 ```notrust
-cargo:rustc-flags=-l static=foo -L native=/path/to/foo
+cargo:rustc-link-lib=static=foo
+cargo:rustc-link-search=native=/path/to/foo
 cargo:root=/path/to/foo
 cargo:libdir=/path/to/foo/lib
 cargo:include=/path/to/foo/include
 ```
 
+The `rustc-link-lib` key indicates that Cargo should pass a `-l` option to
+rustc. Similarly, `rustc-link-search` indicates that Cargo should pass a `-L`
+option.
+
 The `rustc-flags` key is special and indicates the flags that Cargo will
-pass to Rustc. Currently only `-l` and `-L` are accepted.
+pass to Rustc. Currently only `-l` and `-L` are accepted. Using
+`rustc-link-lib` and `rustc-link-search` is more robust.
 
 Any other element is a user-defined metadata that will be passed to
 dependencies. More information about this can be found in the [`links`][links]
@@ -157,7 +163,8 @@ Cargo [configuration location](config.html).
 
 ```toml
 [target.x86_64-unknown-linux-gnu.foo]
-rustc-flags = "-L /path/to/foo -l foo"
+rustc-link-search = ["/path/to/foo"]
+rustc-link-lib = ["foo"]
 root = "/path/to/foo"
 key = "value"
 ```
@@ -165,7 +172,8 @@ key = "value"
 This section states that for the target `x86_64-unknown-linux-gnu` the library
 named `foo` has the metadata specified. This metadata is the same as the
 metadata generated as if the build script had run, providing a number of
-key/value pairs where the `rustc-flags` key is slightly special.
+key/value pairs where the `rustc-flags`, `rustc-link-search`, and
+`rustc-link-lib` keys are slightly special.
 
 With this configuration, if a package declares that it links to `foo` then the
 build script will **not** be compiled or run, and the metadata specified will
@@ -313,7 +321,8 @@ fn main() {
                       .current_dir(&Path::new(&out_dir))
                       .status().unwrap();
 
-    println!("cargo:rustc-flags=-L native={} -l static=hello", out_dir);
+    println!("cargo:rustc-link-search=native={}", out_dir);
+    println!("cargo:rustc-link-lib=static=hello");
 }
 ```
 
index 03d0dd14297dabc16d4d68fed2842143bd5a20bd..20b4557ef2e694200a871276a7a3b550233f4ccf 100644 (file)
@@ -574,6 +574,64 @@ test!(propagation_of_l_flags {
 ", compiling = COMPILING, running = RUNNING).as_slice()));
 });
 
+test!(propagation_of_l_flags_new {
+    let (_, target) = ::cargo::ops::rustc_version().unwrap();
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "foo"
+            version = "0.5.0"
+            authors = []
+            [dependencies.a]
+            path = "a"
+        "#)
+        .file("src/lib.rs", "")
+        .file("a/Cargo.toml", r#"
+            [project]
+            name = "a"
+            version = "0.5.0"
+            authors = []
+            links = "bar"
+            build = "build.rs"
+
+            [dependencies.b]
+            path = "../b"
+        "#)
+        .file("a/src/lib.rs", "")
+        .file("a/build.rs", r#"
+            fn main() {
+                println!("cargo:rustc-link-search=bar");
+            }
+        "#)
+        .file("b/Cargo.toml", r#"
+            [project]
+            name = "b"
+            version = "0.5.0"
+            authors = []
+            links = "foo"
+            build = "build.rs"
+        "#)
+        .file("b/src/lib.rs", "")
+        .file("b/build.rs", "bad file")
+        .file(".cargo/config", format!(r#"
+            [target.{}.foo]
+            rustc-link-search = ["foo"]
+        "#, target).as_slice());
+
+    assert_that(p.cargo_process("build").arg("-v").arg("-j1"),
+                execs().with_status(0)
+                       .with_stdout(format!("\
+[..]
+[..]
+[..]
+[..]
+{running} `[..]a-[..]build-script-build[..]`
+{running} `rustc [..] --crate-name a [..]-L bar[..]-L foo[..]`
+{compiling} foo v0.5.0 (file://[..])
+{running} `rustc [..] --crate-name foo [..] -L bar -L foo`
+", compiling = COMPILING, running = RUNNING).as_slice()));
+});
+
 test!(build_deps_simple {
     let p = project("foo")
         .file("Cargo.toml", r#"
@@ -785,6 +843,32 @@ test!(output_separate_lines {
 ", compiling = COMPILING, running = RUNNING).as_slice()));
 });
 
+test!(output_separate_lines_new {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "foo"
+            version = "0.5.0"
+            authors = []
+            build = "build.rs"
+        "#)
+        .file("src/lib.rs", "")
+        .file("build.rs", r#"
+            fn main() {
+                println!("cargo:rustc-link-search=foo");
+                println!("cargo:rustc-link-lib=static=foo");
+            }
+        "#);
+    assert_that(p.cargo_process("build").arg("-v"),
+                execs().with_status(101)
+                       .with_stdout(format!("\
+{compiling} foo v0.5.0 (file://[..])
+{running} `rustc build.rs [..]`
+{running} `[..]foo-[..]build-script-build[..]`
+{running} `rustc [..] --crate-name foo [..] -L foo -l static=foo`
+", compiling = COMPILING, running = RUNNING).as_slice()));
+});
+
 #[cfg(not(windows))] // FIXME(#867)
 test!(code_generation {
     let p = project("foo")